use std::error::Error;
use std::fmt::{Display, Formatter};
use actix_web::http::StatusCode;
use actix_web::{HttpResponse, ResponseError};
use actix_web::body::BoxBody;
use serde::{Serialize};
use actix_web::error::Error as ActixWebError;

#[derive(Debug, Serialize)]
pub enum MyError {
    DBError(String),
    ActixError(String),
    NotFound(String),
    InvalidInput(String),
}

impl Error for MyError {

}

impl From<ActixWebError> for MyError {
    fn from(err: ActixWebError) -> Self {
        MyError::ActixError(err.to_string())
    }
}

type SQLXError = sqlx::error::Error;

impl From<SQLXError> for MyError {
    fn from(err: SQLXError) -> Self {
        MyError::DBError(err.to_string())
    }
}

impl Display for MyError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self)
    }
}

impl ResponseError for MyError {
    fn status_code(&self) -> StatusCode {
        match self {
            MyError::DBError(_) | MyError::ActixError(_) => { StatusCode::INTERNAL_SERVER_ERROR }
            MyError::NotFound(_) => { StatusCode::NOT_FOUND }
            MyError::InvalidInput(_) => { StatusCode::BAD_REQUEST }
        }
    }

    fn error_response(&self) -> HttpResponse<BoxBody> {
        HttpResponse::build(self.status_code()).json(MyErrorResponse {
            error_message: self.error_response()
        })
    }
}

impl MyError {
    fn error_response(&self) -> String {
        match self {
            MyError::DBError(msg) => {
                println!("Database error occurred：{:?}", msg);
                "Database error".to_string()
            }
            MyError::ActixError(msg) => {
                println!("Server error occurred：{:?}", msg);
                "Internal server error".to_string()
            }
            MyError::NotFound(msg) => {
                println!("Server error occurred：{:?}", msg);
                msg.to_string()
            }
            MyError::InvalidInput(msg) => {
                println!("Server error occurred：{:?}", msg);
                msg.to_string()
            }
        }
    }
}

#[derive(Debug, Serialize)]
pub struct MyErrorResponse {
    error_message: String,
}

